﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;

namespace MuYue.Http {
    /// <summary>
    /// 流的相关拓展
    /// 网络流拓展http协议的接收
    /// </summary>
    public static class StreamExtends {
        private const int defaultBufferLength = 20480;
        /// <summary>
        /// 将字节数组完全写入流中
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="buffer"></param>
        public static void Write(this Stream stream, byte[] buffer) {
            stream.Write(buffer, 0, buffer.Length);
        }

        /// <summary>
        /// 将文本写入流中
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="message"></param>
        public static void Write(this Stream stream, string message) {
            stream.Write(Encoding.ASCII.GetBytes(message));
        }

        /// <summary>
        /// 以Format的方式来构建文本写入流
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="message">模块文本</param>
        /// <param name="extraMessage">参数信息</param>
        public static void Write(this Stream stream, string message, params  object[] extraMessage) {
            stream.Write(string.Format(message, extraMessage));
        }

        /// <summary>
        /// 从网络流中获取返回数据
        /// </summary>
        /// <param name="ns">网络流</param>
        /// <param name="ms">接收数据流如果</param>
        /// <returns></returns>
        public static Encoding GetResponse(this NetworkStream ns, Stream ms) {
            ms = ms ?? new MemoryStream();
            ns.ReadTimeout = 10 * 1000;
            HttpHeader header = new HttpHeader();

            #region 获取http响应头
            var f1 = '\r';
            var f2 = '\n';
            var buffer = new byte[1];
            var fi = 0;
            List<byte> bf = new List<byte>();
            bool firstLine = true;
            int ir;
            while (true) {
                ir = ns.Read(buffer, 0, 1);//对这个接收进行相应的优化处理=>
                if (ir == 0) {
                    throw new Exception("连接异常中断");
                }

                fi = (buffer[0] == f1 || buffer[0] == f2) ? (++fi) : 0;
                if (fi == 0) {
                    bf.Add(buffer[0]);
                }
                if (fi > 1) {
                    //遇到换行符
                    if (fi > 3) { break; }
                    var str = Encoding.ASCII.GetString(bf.ToArray());
                    //Console.WriteLine(str);
                    if (firstLine) {
                        header.AnalyFirstLine(str);
                        firstLine = false;
                    }
                    else {
                        header.Add(str);
                    }
                    bf.Clear();
                }
            }
            if (!string.IsNullOrWhiteSpace(header.ReferUrl)) {
                using (var net = WebEngine.RequestWeb(new Uri(header.ReferUrl))) {
                    return net.GetResponse(ms);
                }
            }

            if (header.Status != System.Net.HttpStatusCode.OK) {
                return header.ContentEncoding;
            }
            #endregion

            if (header.Chuncked || header.ContentLength == 0) {
                #region 获取长度失败了就尝试直接接收
                while (true) {
                    #region Chuncked的长度
                    using (var hs = new MemoryStream(128)) {
                        long contentLength;
                        while (true) {
                            ns.Read(buffer, 0, 1);
                            fi = (buffer[0] == f2 || buffer[0] == f1) ? (++fi) : 0;
                            hs.WriteByte(buffer[0]);
                            if (fi > 1) { break; }
                        }
                        string str = Encoding.ASCII.GetString(hs.ToArray()).Replace("\r\n", "");
                        //解析chunked的长度 使用的16进制存放
                        if (!Int64.TryParse(str, System.Globalization.NumberStyles.HexNumber, null, out contentLength)) {
                            ms.Write(hs.ToArray());
                            header.Chuncked = false;
                        }
                        else {
                            if (contentLength == 0)
                                return header.ContentEncoding;
                            contentLength += 2;
                            header.ContentLength = contentLength;
                        }
                    }
                    #endregion
                    try {
                        if (header.Chuncked) {
                            //Console.WriteLine("chunked:{0}", header.ContentLength);
                            RecvChuncked(ns, header.ContentLength, ms);
                        }
                        else {
                            JustRecv(ns, ms);
                            return header.ContentEncoding;
                        }
                    }
                    catch { return header.ContentEncoding; }
                }
                #endregion
            }
            RecvChuncked(ns, header.ContentLength, ms);
            return header.ContentEncoding;
        }

        /// <summary>
        /// 不知道数据长度进行盲目接收
        /// </summary>
        /// <param name="ns"></param>
        /// <param name="ms"></param>
        private static void JustRecv(NetworkStream ns, Stream ms) {
            var buffer = new byte[defaultBufferLength];
            while (true) {
                var recv = ns.Read(buffer, 0, buffer.Length);
                if (recv == 0)
                    return;
                ms.Write(buffer, 0, recv);
            }
        }


        /// <summary>
        /// 接收Chunked段
        /// </summary>
        /// <param name="ns"></param>
        /// <param name="contentLength"></param>
        /// <param name="ms"></param>
        private static void RecvChuncked(NetworkStream ns, long contentLength, Stream ms) {
            var buffer = new byte[(contentLength > defaultBufferLength) ? defaultBufferLength : contentLength];
            while (contentLength > 0) {
                if (contentLength < buffer.Length)
                    buffer = new byte[contentLength];
                var recv = ns.Read(buffer, 0, buffer.Length);
                ms.Write(buffer, 0, recv);
                contentLength -= recv;
            }
        }
    }
}
